<template>
  <view v-if="showPopup" class="uv-popup" :class="[popupClass, isDesktop ? 'fixforpc-z-index' : '']"
    :style="[{zIndex: zIndex}]">
    <view @touchstart="touchstart">
      <!-- 遮罩层 -->
      <uv-overlay key="1" v-if="maskShow && overlay" :show="showTrans" :duration="duration" :custom-style="overlayStyle"
        :opacity="overlayOpacity" :zIndex="zIndex" @click="onTap"></uv-overlay>
      <uv-transition key="2" :mode="ani" name="content" :custom-style="transitionStyle" :duration="duration"
        :show="showTrans" @click="onTap">
        <view class="uv-popup__content" :style="[contentStyle]" :class="[popupClass]" @click="clear">
          <uv-status-bar v-if="safeAreaInsetTop"></uv-status-bar>
          <slot />
          <uv-safe-bottom v-if="safeAreaInsetBottom"></uv-safe-bottom>
          <view v-if="closeable" @tap.stop="close" class="uv-popup__content__close"
            :class="['uv-popup__content__close--' + closeIconPos]" hover-class="uv-popup__content__close--hover"
            hover-stay-time="150">
            <uv-icon name="close" color="#909399" size="18" bold></uv-icon>
          </view>
        </view>
      </uv-transition>
    </view>
    <!-- #ifdef H5 -->
    <keypress v-if="maskShow" @esc="onTap" />
    <!-- #endif -->
  </view>
</template>

<script>
  // #ifdef H5
  import keypress from './keypress.js'
  // #endif
  import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  /**
  * PopUp 弹出层
  * @description 弹出层组件，为了解决遮罩弹层的问题
  * @tutorial https://www.uvui.cn/components/popup.html
  * @property {String} mode = [top|center|bottom|left|right] 弹出方式
  * 	@value top 顶部弹出
  * 	@value center 中间弹出
  * 	@value bottom 底部弹出
  * 	@value left		左侧弹出
  * 	@value right  右侧弹出
  * @property {Number} duration 动画时长，默认300
  * @property {Boolean} overlay 是否显示遮罩，默认true
  * @property {Boolean} overlayOpacity 遮罩透明度，默认0.5 
  * @property {Object} overlayStyle 遮罩自定义样式
  * @property {Boolean} closeOnClickOverlay = [true|false] 蒙版点击是否关闭弹窗，默认true
  * @property {Number | String} zIndex 弹出层的层级
  * @property {Boolean} safeAreaInsetTop 是否留出顶部安全区（状态栏高度），默认false
  * @property {Boolean} safeAreaInsetBottom 是否为留出底部安全区适配，默认true
  * @property {Boolean} closeable 是否显示关闭图标，默认false
  * @property {Boolean} closeIconPos 自定义关闭图标位置，`top-left`-左上角，`top-right`-右上角，`bottom-left`-左下角，`bottom-right`-右下角，默认top-right
  * @property {String}  bgColor 主窗口背景色
  * @property {String}  maskBackgroundColor 蒙版颜色
  * @property {Boolean} customStyle 自定义样式
  * @event {Function} change 打开关闭弹窗触发，e={show: false}
  * @event {Function} maskClick 点击遮罩触发
  */
  export default {
    name: 'uv-popup',
    components: {
      // #ifdef H5
      keypress
      // #endif
    },
    mixins: [mpMixin, mixin],
    emits: ['change', 'maskClick'],
    props: {
      // 弹出层类型，可选值，top: 顶部弹出层；bottom：底部弹出层；center：全屏弹出层
      // message: 消息提示 ; dialog : 对话框
      mode: {
        type: String,
        default: 'center'
      },
      // 动画时长，单位ms
      duration: {
        type: [String, Number],
        default: 300
      },
      // 层级
      zIndex: {
        type: [String, Number],
        // #ifdef H5
        default: 997
				// #endif
				// #ifndef H5
				default: 10075
        // #endif
      },
      bgColor: {
        type: String,
        default: '#ffffff'
      },
      safeArea: {
        type: Boolean,
        default: true
      },
      // 是否显示遮罩
      overlay: {
        type: Boolean,
        default: true
      },
      // 点击遮罩是否关闭弹窗
      closeOnClickOverlay: {
        type: Boolean,
        default: true
      },
      // 遮罩的透明度，0-1之间
      overlayOpacity: {
        type: [Number, String],
        default: 0.4
      },
      // 自定义遮罩的样式
      overlayStyle: {
        type: [Object, String],
        default: ''
      },
      // 是否为iPhoneX留出底部安全距离
      safeAreaInsetBottom: {
        type: Boolean,
        default: true
      },
      // 是否留出顶部安全距离（状态栏高度）
      safeAreaInsetTop: {
        type: Boolean,
        default: false
      },
      // 是否显示关闭图标
      closeable: {
        type: Boolean,
        default: false
      },
      // 自定义关闭图标位置，top-left为左上角，top-right为右上角，bottom-left为左下角，bottom-right为右下角
      closeIconPos: {
        type: String,
        default: 'top-right'
      },
      // mode=center，也即中部弹出时，是否使用缩放模式
      zoom: {
        type: Boolean,
        default: true
      },
      round: {
        type: [Number, String],
        default: 0
      },
      ...uni.$uv?.props?.popup
    },
    watch: {
      /**
       * 监听type类型
       */
      type: {
        handler: function (type) {
          if (!this.config[type]) return
          this[this.config[type]](true)
        },
        immediate: true
      },
      isDesktop: {
        handler: function (newVal) {
          if (!this.config[newVal]) return
          this[this.config[this.mode]](true)
        },
        immediate: true
      },
      // H5 下禁止底部滚动
      showPopup(show) {
        // #ifdef H5
        // fix by mehaotian 处理 h5 滚动穿透的问题
        document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
        // #endif
      }
    },
    data() {
      return {
        ani: [],
        showPopup: false,
        showTrans: false,
        popupWidth: 0,
        popupHeight: 0,
        config: {
          top: 'top',
          bottom: 'bottom',
          center: 'center',
          left: 'left',
          right: 'right',
          message: 'top',
          dialog: 'center',
          share: 'bottom'
        },
        transitionStyle: {
          position: 'fixed',
          left: 0,
          right: 0
        },
        maskShow: true,
        mkclick: true,
        popupClass: this.isDesktop ? 'fixforpc-top' : 'top',
        direction: ''
      }
    },
    computed: {
      isDesktop() {
        return this.popupWidth >= 500 && this.popupHeight >= 500
      },
      bg() {
        if (this.bgColor === '' || this.bgColor === 'none' || this.$uv.getPx(this.round) > 0) {
          return 'transparent'
        }
        return this.bgColor
      },
      contentStyle() {
        const style = {};
        if (this.bgColor) {
          style.backgroundColor = this.bg
        }
        if (this.round) {
          const value = this.$uv.addUnit(this.round)
          const mode = this.direction ? this.direction : this.mode
          style.backgroundColor = this.bgColor
          if (mode === 'top') {
            style.borderBottomLeftRadius = value
            style.borderBottomRightRadius = value
          } else if (mode === 'bottom') {
            style.borderTopLeftRadius = value
            style.borderTopRightRadius = value
          } else if (mode === 'center') {
            style.borderRadius = value
          }
        }
        return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
      }
    },
    // #ifndef VUE3
    // TODO vue2
    destroyed() {
      this.setH5Visible()
    },
    // #endif
    // #ifdef VUE3
    // TODO vue3
    unmounted() {
      this.setH5Visible()
    },
    // #endif
    created() {
      // TODO 处理 message 组件生命周期异常的问题
      this.messageChild = null
      // TODO 解决头条冒泡的问题
      this.clearPropagation = false
    },
    methods: {
      setH5Visible() {
        // #ifdef H5
        // fix by mehaotian 处理 h5 滚动穿透的问题
        document.getElementsByTagName('body')[0].style.overflow = 'visible'
        // #endif
      },
      /**
       * 公用方法，不显示遮罩层
       */
      closeMask() {
        this.maskShow = false
      },
      // TODO nvue 取消冒泡
      clear(e) {
        // #ifndef APP-NVUE
        e.stopPropagation()
        // #endif
        this.clearPropagation = true
      },

      open(direction) {
        // fix by mehaotian 处理快速打开关闭的情况
        if (this.showPopup) {
          return
        }
        let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
        if (!(direction && innerType.indexOf(direction) !== -1)) {
          direction = this.mode
        } else {
          this.direction = direction;
        }
        if (!this.config[direction]) {
          return this.$uv.error(`缺少类型：${direction}`);
        }
        this[this.config[direction]]()
        this.$emit('change', {
          show: true,
          type: direction
        })
      },
      close(type) {
        this.showTrans = false
        this.$emit('change', {
          show: false,
          type: this.mode
        })
        clearTimeout(this.timer)
        // // 自定义关闭事件
        this.timer = setTimeout(() => {
          this.showPopup = false
        }, 300)
      },
      // TODO 处理冒泡事件，头条的冒泡事件有问题 ，先这样兼容
      touchstart() {
        this.clearPropagation = false
      },
      onTap() {
        if (this.clearPropagation) {
          // fix by mehaotian 兼容 nvue
          this.clearPropagation = false
          return
        }
        this.$emit('maskClick')
        if (!this.closeOnClickOverlay) return
        this.close()
      },
      /**
       * 顶部弹出样式处理
       */
      top(type) {
        this.popupClass = this.isDesktop ? 'fixforpc-top' : 'top'
        this.ani = ['slide-top']
        this.transitionStyle = {
          position: 'fixed',
          zIndex: this.zIndex,
          left: 0,
          right: 0,
          backgroundColor: this.bg
        }
        // TODO 兼容 type 属性 ，后续会废弃
        if (type) return
        this.showPopup = true
        this.showTrans = true
        this.$nextTick(() => {
          if (this.messageChild && this.mode === 'message') {
            this.messageChild.timerClose()
          }
        })
      },
      /**
       * 底部弹出样式处理
       */
      bottom(type) {
        this.popupClass = 'bottom'
        this.ani = ['slide-bottom']
        this.transitionStyle = {
          position: 'fixed',
          zIndex: this.zIndex,
          left: 0,
          right: 0,
          bottom: 0,
          backgroundColor: this.bg
        }
        // TODO 兼容 type 属性 ，后续会废弃
        if (type) return
        this.showPopup = true
        this.showTrans = true
      },
      /**
       * 中间弹出样式处理
       */
      center(type) {
        this.popupClass = 'center'
        this.ani = this.zoom ? ['zoom-in', 'fade'] : ['fade'];
        this.transitionStyle = {
          position: 'fixed',
          zIndex: this.zIndex,
          /* #ifndef APP-NVUE */
          display: 'flex',
          flexDirection: 'column',
          /* #endif */
          bottom: 0,
          left: 0,
          right: 0,
          top: 0,
          justifyContent: 'center',
          alignItems: 'center'
        }
        // TODO 兼容 type 属性 ，后续会废弃
        if (type) return
        this.showPopup = true
        this.showTrans = true
      },
      left(type) {
        this.popupClass = 'left'
        this.ani = ['slide-left']
        this.transitionStyle = {
          position: 'fixed',
          zIndex: this.zIndex,
          left: 0,
          bottom: 0,
          top: 0,
          backgroundColor: this.bg,
          /* #ifndef APP-NVUE */
          display: 'flex',
          flexDirection: 'column'
          /* #endif */
        }
        // TODO 兼容 type 属性 ，后续会废弃
        if (type) return
        this.showPopup = true
        this.showTrans = true
      },
      right(type) {
        this.popupClass = 'right'
        this.ani = ['slide-right']
        this.transitionStyle = {
          position: 'fixed',
          zIndex: this.zIndex,
          bottom: 0,
          right: 0,
          top: 0,
          backgroundColor: this.bg,
          /* #ifndef APP-NVUE */
          display: 'flex',
          flexDirection: 'column'
          /* #endif */
        }
        // TODO 兼容 type 属性 ，后续会废弃
        if (type) return
        this.showPopup = true
        this.showTrans = true
      }
    }
  }
</script>
<style lang="scss" scoped>
  .uv-popup {
    position: fixed;
    /* #ifndef APP-NVUE */
    z-index: 99;

    /* #endif */
    &.top,
    &.left,
    &.right {
      /* #ifdef H5 */
      top: var(--window-top);
      /* #endif */
      /* #ifndef H5 */
      top: 0;
      /* #endif */
    }

    .uv-popup__content {
      /* #ifndef APP-NVUE */
      display: block;
      overflow: hidden;
      /* #endif */
      position: relative;
      padding-bottom: 10px;

      &.left,
      &.right {
        /* #ifdef H5 */
        padding-top: var(--window-top);
        /* #endif */
        /* #ifndef H5 */
        padding-top: 0;
        /* #endif */
        flex: 1;
      }

      &__close {
        position: absolute;

        &--hover {
          opacity: 0.4;
        }
      }

      &__close--top-left {
        top: 15px;
        left: 15px;
      }

      &__close--top-right {
        top: 15px;
        right: 15px;
      }

      &__close--bottom-left {
        bottom: 15px;
        left: 15px;
      }

      &__close--bottom-right {
        right: 15px;
        bottom: 15px;
      }
    }
  }

  .fixforpc-z-index {
    /* #ifndef APP-NVUE */
    z-index: 999;
    /* #endif */
  }

  .fixforpc-top {
    top: 0;
  }
</style>